/*********************************************************************
 * INCLUDES
 */
#include "string.h"
#include "bcomdef.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"
#include "ancs.h"
#include <stdlib.h>

/*********************************************************************
 * MACROS
 */

// Maximum attribute parameter length
#define MAX_ATTR_PARAM_LEN            0x14      // 20
#define MAX_NOTIF_ATTR_DATA_LEN_PER_BLE_PKE         (20 - 1 - 4 - 1 - 2)      // That is 20 minus DommandID(1), mius NotificationUID(4), minus AttributeID(1), and minus Attribute length(2)

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

typedef struct
{
  uint8_t categoryID;           //
  uint8_t notificationUID[4];   //
} notifCatType_t;

typedef struct notifCatTypeList_t
{
  notifCatType_t notifCategory;           //
  struct notifCatTypeList_t *pNext;   //
} notifCatTypeList_t;

static notifCatTypeList_t *pNotifCatListStart = NULL;

/*********************************************************************
 * GLOBAL VARIABLES
 */
uint16_t Ancs_connHandle;
extern uint8_t serviceDiscoveryState;
   
/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */
#if 0
static const char *AncsAppNotifSrcEvtIDStr[] =
{
  "EvtID: Notif Added",        //  Notification Added
  "EvtID: Notif Modified",     //  Notification Modified
  "EvtID: Notif Removed"       //  Notification Removed
};

static const char *AncsAppNotifSrcCatStr[] =
{
  "CatID: Other",             //  Other Category
  "CatID: Incoming Call",     //  Incooming Call
  "CatID: Missed Call",       //  Missed Call
  "CatID: Voice Mail",        //  Voice Mail
  "CatID: Social",            //  Socials
  "CatID: Schedule",          //  Schedule
  "CatID: Email",             //  Email
  "CatID: News",              //  News
  "CatID: Health & Fitness",  //  Health & Fitness
  "CatID: Biz & Finance",     //  Biz & Finance
  "CatID: Location",          //  Location
  "CatID: Entertainment"      //  Entertainment
};
#endif

uint8_t configCCCDState = CCCD_CONFIG_NOTIF;

// Data source state, for getting notification attribute ID values
enum
{
  NOTI_ATTR_ID_BEGIN = 0x00,
  
  NOTI_ATTR_ID_APPID,
  NOTI_ATTR_ID_TITLE,                
  NOTI_ATTR_ID_SUBTITLE,                    // 
  NOTI_ATTR_ID_MESSAGE,                     // Get the message content
  NOTI_ATTR_ID_MESSAGE_SIZE,
  NOTI_ATTR_ID_DATE,
  
  NOTI_ATTR_ID_END                       
};
static uint8_t notiAttrState = NOTI_ATTR_ID_BEGIN;

enum
{
  NOTI_ATTR_FIRST_PKT = 0x00,
  NOTI_ATTR_CONTINUE_PKT
};
static uint8_t nitiAttrPktRcvState = NOTI_ATTR_FIRST_PKT;
static uint8_t dataBuf[100] = {'\0'};
static uint16_t dataLen = 0x0000;
static uint8_t NotiUID[4] = {0x00}; 

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void Ancs_notifCatAdd2List(uint8_t categoryID, uint8_t *pNotifUID);
static notifCatTypeList_t* Ancs_notifCatFindList(uint8_t *pNotifUID);
static void Ancs_notifCatDelList(uint8_t *pNotifUID);
static void Ancs_notifCatDelListAll(void);
#if 0
static char* convertHex2Str(uint8_t *pHexValue, uint8_t len);
#endif
static uint8_t Ancs_CCCDConfig(uint16_t attrHdl, uint8_t isEnable);
static uint8_t Ancs_getNotifAttr(uint8_t *pNotificationUID, uint8_t attributeID, uint16_t len);
static void Ancs_handleNotifAttrRsp(uint8_t *pNotificationUID);

/*********************************************************************
 * @fn      Ancs_controlGattMsg
 *
 * @brief   Handle read / write. 
 *
 * @param   pMsg - GATT message.
 *
 * @return  none
 */
void Ancs_controlGattMsg(gattMsgEvent_t *pMsg)
{  
  if ((pMsg->method == ATT_READ_RSP || pMsg->method == ATT_WRITE_RSP) &&
       (pMsg->hdr.status == SUCCESS))
  {
    if (configCCCDState == CCCD_CONFIG_DATA)
    {
      Ancs_subsDataSrc();
      configCCCDState = CCCD_CONFIG_DONE;
    }
  }

}

/*********************************************************************
 * @fn      Ancs_notifCatAdd2List
 *
 * @brief   Add a Notification & Category to list. 
 *
 * @param   categoryID - category ID of the notification.
 *          pNotifUID  - notification UID.   
 *
 * @return  none
 */
static void Ancs_notifCatAdd2List(uint8_t categoryID, uint8_t *pNotifUID)
{
  notifCatTypeList_t *pNew;
  notifCatTypeList_t *pSearch;
  
  pNew = malloc(sizeof(notifCatTypeList_t));
  if (pNew == NULL)
    return;
  
  // Store categoryID and notification ID.
  pNew->notifCategory.categoryID = categoryID;
  VOID memcpy(pNew->notifCategory.notificationUID, pNotifUID, 4);
  pNew->pNext = NULL;
  
  if (pNotifCatListStart == NULL) // New list
  {
    pNotifCatListStart = pNew;
  }
  else  // Add to the last position of the list
  {
    pSearch = pNotifCatListStart;
    while (pSearch->pNext != NULL)
    {
      pSearch = pSearch->pNext;
    }
    pSearch->pNext = pNew;
  }
}

/*********************************************************************
 * @fn      Ancs_notifCatFindList
 *
 * @brief   Find a Notification & Category in the existing list. 
 *
 * @param   pNotifUID  - notification UID.   
 *
 * @return  none
 */
static notifCatTypeList_t* Ancs_notifCatFindList(uint8_t *pNotifUID)
{
  notifCatTypeList_t *pSearch;
  uint8_t notifUID[4];
  
  VOID memcpy(notifUID, pNotifUID, 4);
  
   pSearch = pNotifCatListStart;
   while ((memcmp(notifUID, pSearch->notifCategory.notificationUID, 4) != 0) && pSearch != NULL)
   {
     pSearch = pSearch->pNext;
   }
   
   if (pSearch == NULL) // Not in the list
     return NULL;
   else
     return pSearch;
}


/*********************************************************************
 * @fn      Ancs_notifCatDelList
 *
 * @brief   Delete a Notification & Category in the existing list. 
 *
 * @param   pNotifUID  - notification UID.   
 *
 * @return  none
 */
static void Ancs_notifCatDelList(uint8_t *pNotifUID)
{
  notifCatTypeList_t *pSearch;
  notifCatTypeList_t *pFound;
  
  uint8_t notifUID[4];
  
  VOID memcpy(notifUID, pNotifUID, 4);
  
   pSearch = pNotifCatListStart;
   while ((memcmp(notifUID, pSearch->pNext->notifCategory.notificationUID, 4) != 0) && pSearch->pNext != NULL)
   {
     pSearch = pSearch->pNext;
   }
   
   if (pSearch->pNext == NULL) // Not found in the list, no need to delete
   {
     return;
   }
   else
   {
     pFound = pSearch->pNext;   // We have found it, it is in the next node of the current one, save it.
     pSearch->pNext = pSearch->pNext->pNext; // Remove the node we want to delete from the list.
     
     free(pFound); // Free it.
   }
}


/*********************************************************************
 * @fn      Ancs_notifCatDelListAll
 *
 * @brief   Delete the whole list of Notification & Category. 
 *
 * @param   pNotifUID  - notification UID.   
 *
 * @return  none
 */
static void Ancs_notifCatDelListAll(void)
{
   notifCatTypeList_t *pSearch;
   notifCatTypeList_t *p;
   
   pSearch = pNotifCatListStart;

   while(pSearch != NULL)
   {
     p = pSearch;
     pSearch = pSearch->pNext;
     free(p);
     p = NULL;
   }
   pNotifCatListStart = NULL;
}


/*********************************************************************
 * @fn      Ancs_indGattMsg
 *
 * @brief   Handle notifications. 
 *
 * @param   pMsg - GATT message.
 *
 * @return  none
 */
void Ancs_notifGattMsg(gattMsgEvent_t *pMsg)
{
  uint8_t i;
  
  // Look up the handle in the handle cache
  for (i = 0; i < HDL_CACHE_LEN; i++)
  {
    if (pMsg->msg.handleValueNoti.handle == Ancs_handleCache[i])
    {
      break;
    }
  }

  // Perform processing for this handle 
  switch (i)
  {
    case HDL_ANCS_NTF_NOTIF_START:
      // Display more detailed info later

       
      break;

      case HDL_ANCS_DATA_SRC_START:
       
        break;
 
      default:
      break;
  }
  
}

/*********************************************************************
 * @fn      Ancs_CCCDConfig
 *
 * @brief   subscribe Notification Source. 
 *
 * @param   none.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
static uint8_t Ancs_CCCDConfig(uint16_t attrHdl, uint8_t isEnable)
{
  uint8_t status = SUCCESS;
      
  attWriteReq_t req;

  return status;
}

/*********************************************************************
 * @fn      Ancs_subsNotifSrc
 *
 * @brief   subscribe Notification Source. 
 *
 * @param   none.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
uint8_t Ancs_subsNotifSrc(void)
{
  uint8_t status = SUCCESS;
      
  // Empty notification list first
  Ancs_notifCatDelListAll();
  
  status = Ancs_CCCDConfig(Ancs_handleCache[HDL_ANCS_NTF_CCCD], TRUE);    
  
  return status;
}

/*********************************************************************
 * @fn      Ancs_unSubsNotifSrc
 *
 * @brief   subscribe Notification Source. 
 *
 * @param   none.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
uint8_t Ancs_unSubsNotifSrc(void)
{
  uint8_t status = SUCCESS;
      
  status = Ancs_CCCDConfig(Ancs_handleCache[HDL_ANCS_NTF_CCCD], FALSE);    
  
  return status;
}


/*********************************************************************
 * @fn      Ancs_subsDataSrc
 *
 * @brief   subscribe Data Source. 
 *
 * @param   none.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
uint8_t Ancs_subsDataSrc(void)
{
  uint8_t status = SUCCESS;
      
  status = Ancs_CCCDConfig(Ancs_handleCache[HDL_ANCS_DATA_SRC_CCCD], TRUE);    
  
  return status;
}

/*********************************************************************
 * @fn      Ancs_unSubsDataSrc
 *
 * @brief   subscribe Data Source. 
 *
 * @param   none.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
uint8_t Ancs_unSubsDataSrc(void)
{
  uint8_t status = SUCCESS;
      
  status = Ancs_CCCDConfig(Ancs_handleCache[HDL_ANCS_DATA_SRC_CCCD], FALSE);    
  
  return status;
}


/*********************************************************************
 * @fn      Ancs_getNotifAttr
 *
 * @brief   Get notification attributes. 
 *
 * @param   attrID - attributes ID.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
static uint8_t Ancs_getNotifAttr(uint8_t *pNotificationUID, uint8_t attributeID, uint16_t len)
{
  uint8_t status = SUCCESS;
  uint8_t cmdLen = 8;
  if (len == 0)
    cmdLen = 6;
  
  // Do a write
  attWriteReq_t req;
   
  return status;
}


/*********************************************************************
 * @fn      Ancs_handleNotifAttrRsp
 *
 * @brief   Handle response value of Notification Attrbutes from iOS device. 
 *
 * @param   attrID - attributes ID.
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
static void Ancs_handleNotifAttrRsp(uint8_t *pNotificationUID)
{
  notifCatTypeList_t *pNode;
  
  switch (notiAttrState)
  {
    case NOTI_ATTR_ID_BEGIN:
      {
        Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDAppIdentifier, 0);           // No length per spec
        notiAttrState = NOTI_ATTR_ID_APPID;
      }
      break;
  
    case NOTI_ATTR_ID_APPID:
      {
        // To get NotificationAttributeIDTitle, that is the calling people / number
        // To do this, write to Notification Control Point
        // Missed call info archive
        Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDTitle, 30);
        notiAttrState = NOTI_ATTR_ID_TITLE;
      }
      break;
      
    case NOTI_ATTR_ID_TITLE:  
      {
        pNode = Ancs_notifCatFindList(pNotificationUID);
        if (pNode->notifCategory.categoryID != CategoryIDSocial) // Not messages
        {
          Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDDate, 0);            // No length per spec
          notiAttrState = NOTI_ATTR_ID_DATE;
        }
        else
        {
          Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDSubtitle, 20);
          notiAttrState = NOTI_ATTR_ID_SUBTITLE;
        }
      }
      break;
      
    case NOTI_ATTR_ID_SUBTITLE:
      {
        Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDMessage, 100);
        notiAttrState = NOTI_ATTR_ID_MESSAGE;
      }
      break;
    
    case NOTI_ATTR_ID_MESSAGE:
      {
        Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDMessageSize, 0);     // No length per spec
        notiAttrState = NOTI_ATTR_ID_MESSAGE_SIZE;
        //notiAttrState = NOTI_ATTR_ID_END;
      }
      break;
    
    case NOTI_ATTR_ID_MESSAGE_SIZE:
      {
        Ancs_getNotifAttr(pNotificationUID, NotificationAttributeIDDate, 0);            // No length per spec
        notiAttrState = NOTI_ATTR_ID_DATE;
      }
      break;
    
    case NOTI_ATTR_ID_DATE:
      {
        notiAttrState = NOTI_ATTR_ID_END;
      }
      break;
      
    default:
      break;

  }
  
}

#if 0
/*********************************************************************
 * @fn      convertHex2Str
 *
 * @brief   Internal function, for convert Hex to string. (This one
 *          now speicially for NotificationUID useage)
 *
 * @param   pHexValue - hex values to be converted.
 *          len - length of the hex value in byte
 *
 * @return  uint8_t SUCCESS/FAILURE
 */
static char* convertHex2Str(uint8_t *pHexValue, uint8_t len)
{
  uint8_t     charCnt;
  char        hex[] = "0123456789ABCDEF";
  static char str[(2*4)+3 + 7] = {'\0'};             // length of NotificationUID is 32 bit, 4 bytes
  char        *pStr = str;

  *pStr++ = 'N';
  *pStr++ = 'o';
  *pStr++ = 'U';
  *pStr++ = 'I';
  *pStr++ = 'D';
  *pStr++ = ':';
  *pStr++ = ' ';
  *pStr++ = '0';
  *pStr++ = 'x';

  // Start from end of addr
  pHexValue += len;

  for (charCnt = len; charCnt > 0; charCnt--)
  {
    *pStr++ = hex[*--pHexValue >> 4];
    *pStr++ = hex[*pHexValue & 0x0F];
  }

  //pStr = NULL;

  return str;
}
#endif

/*********************************************************************
*********************************************************************/
